//
//  IMB_NetWorkAccessOperationWithFiles.m
//  BabyNow
//
//  Created by iMobile on 14-4-8.
//  Copyright (c) 2014年 iMobile. All rights reserved.
//

#import "IMB_NetWorkAccessOperationWithFilesUpload.h"

NSString *const kContentType = @"Content-Type";
NSString *const vContentType = @"multipart/form-data; boundary=imobile";

// 换行符
NSString *const returnNewLineSymbol = @"\r\n";

// 普通参数数据边界
NSString *const paramsDataBoundary = @"\r\n--imobile\r\n";

// 文件表单参数数据边界
NSString *const paramsFileDataBoundary = @"--imobile\r\n";

// 参数结束边界
NSString *const paramsEndDataBoundary = @"\r\n--imobile--\r\n";

// 普通参数名称内容
#define PARAM_NAME_CONTENT @"Content-Disposition: form-data; name=\"%@\"\r\n"
// 普通参数值内容
#define PARAM_VALUE_CONTENT @"\r\n%@\r\n"

// 文件参数名称内容
#define PARAM_NAME_FILE_CONTENT @"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n"


@interface IMB_NetWorkAccessOperationWithFilesUpload()<NSURLConnectionDataDelegate>
{
    
    /**
     *  上传地址字符串
     */
    NSString *uploadUrlStr_;
   
    /**
     *  文件参数名称
     */
    NSString *paramsFileName_;
    
    /**
     *  文件的mime类型
     */
    NSString *mimeType_;
    
    /**
     *  普通表单参数内容字典
     */
    NSDictionary *paramsDict_;
    
    /**
     *  文件数据内容字典
     */
    NSDictionary *paramsFileDataDict_;
    
    // add by jason yan 2014-04-30
    /**
     *  文件数据内容字典
     */
    NSArray *paramsFileDataArr_;
    
    
    /**
     *  网络连接对象
     */
    NSURLConnection *connection_;
    
    //
    /**
     *  请求体的内容
     */
    NSMutableData *bodyData_;
    
    /**
     *  处理完毕后的消息
     */
    NSString *message_;
    
    /**
     *  处理完毕后的响应数据
     */
    NSMutableData *responseData_;
    
    /**
     *  已经结束请求处理回调Block
     */
    DidFinishRequestHandler block_;
    
    // add by jason yan 2014-05-29
    
    /**
     *  是否在执行
     */
    BOOL executing_;
    
    /**
     *  是否已经结束
     */
    BOOL finished_;

}

/**
 *  向请求体中添加普通表单内容
 */
- (void)addParamsValuesToRequestBody;

/**
 *  向请求体中添加文件数据内容
 */
- (void)addFileParamsValuesToRequestBody;

/**
 * 网络访问完毕后的处理
 */
- (void)netAccessComplete;

/**
 *  根据响应状态码获取数据响应错误消息
 *
 *  @param statusCode 状态码
 *
 *  @return 访问错误
 */
- (NSString*)responseErrMsgForStatusCode:(long)statusCode;

/**
 *  根据网络请求错误码返回错误消息
 *
 *  @param errorCode 网络错误代码
 *
 *  @return 错误消息
 */
- (NSString*)netErrorrMsgForCode:(NSInteger)errorCode;

@end


@implementation IMB_NetWorkAccessOperationWithFilesUpload

#pragma mark - NSConnectionDataDelegate method
/**
 *  网络请求失败处理
 *
 *  @param connection 网络连接对象
 *  @param error      错误封装
 */
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
#warning 开发阶段使用系统错误提示，发布前替换
    //message_ = [error description];
    message_ = [self netErrorrMsgForCode:error.code];
    [self netAccessComplete];
}

/**
 *  已接受到网络响应
 *
 *  @param connection 网络连接对象
 *  @param response   响应对象
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSHTTPURLResponse *httpUrlResponse = (NSHTTPURLResponse*)response;
    NSString *responseErrMsg = [self responseErrMsgForStatusCode:httpUrlResponse.statusCode];
    if (responseErrMsg) {
        message_ = responseErrMsg;
    }else{
        responseData_ = [NSMutableData data];
    }
}

/**
 *  接收到网络数据
 *
 *  @param connection 网络连接对象
 *  @param data       网络数据
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [responseData_ appendData:data];
}

/**
 *  已结束网络加载
 *
 *  @param connection 网络连接对象
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    [self netAccessComplete];
}

/**
 *  已发送数据
 *
 *  @param connection                网络连接对象
 *  @param bytesWritten              最近传输的数据
 *  @param totalBytesWritten         以传输数据量
 *  @param totalBytesExpectedToWrite 总共需要传书的数据量
 */
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
    self.progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
}


#pragma mark - Extend method

// 根据网络请求错误码返回错误消息
- (NSString*)netErrorrMsgForCode:(NSInteger)errorCode{
    switch (errorCode) {
        case kCFURLErrorBadURL:
            return CONNECTION_ERROR_MSG_FOR_BAD_URL;
            break;
        case kCFURLErrorTimedOut:
            return CONNECTION_ERROR_MSG_FOR_TIME_OUT;
            break;
        case kCFURLErrorUnsupportedURL:
            return CONNECTION_ERROR_MSG_FOR_UNSUPPORT_URL;
            break;
        case kCFURLErrorCannotFindHost:
            return CONNECTION_ERROR_MSG_FOR_NOT_FIND_HOST;
            break;
        default:
            NSLog(@"###### 未知netErrorCode:%ld",errorCode);
            return CONNECTION_ERROR_MSG_FOR_UNKNOW;
            break;
    }
    return nil;
}

// 根据响应状态码获取数据响应错误消息
- (NSString*)responseErrMsgForStatusCode:(long)statusCode{
    
    if (statusCode > ENUM_HTTP_STATUS_NET_IF_SERVER_EXCEPTION) {
        return NET_ERROR_MSG_FOR_NET_IF_EXCEPTION;
    }
    switch (statusCode) {
        case ENUM_HTTP_STATUS_NET_IF_REQUEST_METHOD_ERROR:
            return NET_ERROR_MSG_FOR_NET_IF_REQUEST_METHOD_ERROR;
            break;
        case ENUM_HTTP_STATUS_NET_IF_NOT_EXIST:
            return NET_ERROR_MSG_FOR_NET_IF_NOT_EXIST;
            break;
        case ENUM_HTTP_STATUS_NET_IF_NOT_IMP:
            return NET_ERROR_MSG_FOR_NET_IF_NOT_IMP;
            break;
        case ENUM_HTTP_STATUS_OK:
            return nil;
        default:
            NSLog(@"###### 未知statusCode:%ld",statusCode);
            return NET_ERROR_MSG_FOR_HTTP_STATUS_UNKNOW;
            break;
    }
    
    return nil;
}



/**
 *  网络访问完毕
 */
- (void)netAccessComplete{
    dispatch_sync(dispatch_get_main_queue(), ^{
        
        // add by jason yan 2014-05-29
        [self willChangeValueForKey:@"isFinished"];
        [self willChangeValueForKey:@"isExecuting"];
        
        block_(responseData_,message_);
        
        // add by jason yan 2014-05-29
        executing_ = NO;
        finished_ = YES;
        [self didChangeValueForKey:@"isExecuting"];
        [self didChangeValueForKey:@"isFinished"];
    });
}


// 向请求体中添加普通表单内容
- (void)addParamsValuesToRequestBody{
    
    for (NSString *paramName in paramsDict_) {
        
        // 设置参数边界
        [bodyData_ appendData:[paramsDataBoundary dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加参数内容
        NSString *paramNameContent = [NSString stringWithFormat:PARAM_NAME_CONTENT,paramName];
        [bodyData_ appendData:[paramNameContent dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加参数值
        NSString *paramValueContent = [NSString stringWithFormat:PARAM_VALUE_CONTENT,paramsDict_[paramName]];
        [bodyData_ appendData:[paramValueContent dataUsingEncoding:NSUTF8StringEncoding]];
    }
}

// 向请求体中添加文件数据内容
- (void)addFileParamsValuesToRequestBody{
    
    int i = 1;
    
    for (NSDictionary *fileDict in paramsFileDataArr_) {
        
        [bodyData_ appendData:[paramsDataBoundary dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 设置参数边界
        [bodyData_ appendData:[paramsFileDataBoundary dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加文件参数名称内容
        NSString *paramFileNameContent = [NSString stringWithFormat:PARAM_NAME_FILE_CONTENT,[paramsFileName_ stringByAppendingFormat:@"%d",i++],fileDict[kFileName]];
        
        [bodyData_ appendData:[paramFileNameContent dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加文件MIME类型
        NSString *mimeTypeContent = [NSString stringWithFormat:@"Content-Type:%@\r\n\r\n",fileDict[kFileMimeType]];
        [bodyData_ appendData:[mimeTypeContent dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加文件数据
        [bodyData_ appendData:fileDict[kFileData]];
    }
    
}

#pragma mark - Interface method

// 初始化方法
- (id)initWithUploadUrlStr:(NSString*)uploadUrlStr
              paramsValues:(NSDictionary*)paramsDict
            paramsFileName:(NSString*)paramsFileName
            paramsFileData:(NSArray*)paramsFileData
           completeHandler:(DidFinishRequestHandler)block{
    if (self = [super init]) {
        
        // 成员变量初始化
        
        // 1. 上传地址字符串
        uploadUrlStr_ = uploadUrlStr;
        
        // 2. 普通表单参数
        paramsDict_ = paramsDict;
        
        // 3. 文件参数名称
        paramsFileName_ = paramsFileName;
        
        // 4. 文件表单内容数据
        paramsFileDataArr_ = paramsFileData;
        
        // 5. 处理完毕后的回调
        block_ = block;
        
    }
    return self;
}


// 初始化方法
- (id)initWithUploadUrlStr:(NSString *)uploadUrlStr
              paramsValues:(NSDictionary *)paramsDict
            paramsFileName:(NSString *)paramsFileName
            paramsFileData:(NSDictionary *)paramsFileDataDict
              fileMimeType:(NSString *)mimeType
           completeHandler:(DidFinishRequestHandler)block{
    
    if (self = [super init]) {
        
        // 成员变量初始化
        
        // 1. 上传地址字符串
        uploadUrlStr_ = uploadUrlStr;
        
        // 2. 普通表单参数
        paramsDict_ = paramsDict;
        
        // 3. 文件参数名称
        paramsFileName_ = paramsFileName;
        
        // 4. 文件表单内容数据
        paramsFileDataDict_ = paramsFileDataDict;
        
        // 5. 文件内容类型
        mimeType_ = mimeType;
        
        // 6. 处理完毕后的回调
        block_ = block;
        
    }
    return self;
}



// 取消上传
- (void)cancelUpload{
    [connection_ cancel];
    dispatch_sync(dispatch_get_main_queue(), ^{
        
        // add by jason yan 2014-05-29
        [self willChangeValueForKey:@"isFinished"];
        [self willChangeValueForKey:@"isExecuting"];
        
        block_(nil,nil);
        
        // add by jason yan 2014-05-29
        executing_ = NO;
        finished_ = YES;
        [self didChangeValueForKey:@"isExecuting"];
        [self didChangeValueForKey:@"isFinished"];
    });
}


/**
 *  NSOperation类的main方法
 */
- (void)main{
    @autoreleasepool {
        
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:uploadUrlStr_]];
        request.HTTPMethod = NET_REQUEST_POST;
        bodyData_ = [NSMutableData data];
        
        // 设置请求头信息-数据类型
        [request setValue:vContentType forHTTPHeaderField:kContentType];
        
        // 添加普通表单内容
        [self addParamsValuesToRequestBody];
        
        // 添加换行符
        //[bodyData_ appendData:[returnNewLineSymbol dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 添加文件表单内容
        [self addFileParamsValuesToRequestBody];
        
        // 设置参数结束
        [bodyData_ appendData:[paramsEndDataBoundary dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 设置请求体
        request.HTTPBody = bodyData_;
        
        // 创建Connection对象并发起异步请求
        connection_ = [NSURLConnection connectionWithRequest:request delegate:self];        
        
        
        [[NSRunLoop currentRunLoop]runUntilDate:[NSDate distantFuture]];
    }
}

#pragma mark - Override NSOperation method

- (void)start {
    // Always check for cancellation before launching the task.
    if ([self isCancelled])
    {
        // Must move the operation to the finished state if it is canceled.
        [self willChangeValueForKey:@"isFinished"];
        finished_ = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }
    
    // If the operation is not canceled, begin executing the task.
    [self willChangeValueForKey:@"isExecuting"];
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
    executing_ = YES;
    [self didChangeValueForKey:@"isExecuting"];
}

- (BOOL)isConcurrent {
    return YES;
}

- (BOOL)isExecuting {
    return executing_;
}

- (BOOL)isFinished {
    return finished_;
}


@end
